package com.globalwave.pos.payment.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.globalwave.base.BaseServiceImpl;
import com.globalwave.common.C;
import com.globalwave.common.exception.BusinessException;
import com.globalwave.pos.payment.entity.Currency;
import com.globalwave.pos.payment.entity.PaymentLine;
import com.globalwave.pos.payment.entity.PaymentMethod;
import com.globalwave.pos.payment.entity.RecievableDetailInf;


@Service("paymentBO")
@Scope("prototype")
@Transactional
public class PaymentBO extends BaseServiceImpl  {
	
	private PaymentLineBO paymentLineBO ;
	
	/**
	 * 
	 * @param payableOrder
	 * @param payments 付款详细信息，但非最终保存到订单的付款明细
	 * @param recievableDetails
	 * @throws Exception
	 */
	public void checkOut(
			Payable payableOrder, 
			List<PaymentLine> payments, 
			List<RecievableDetailInf> recievableDetails,
			Short ref_source,
			Long order_id) throws Exception {

		
		// 检查是否已经完成付款操作
		PaymentAmount pAmount = new PaymentAmount() ;
		
		for (RecievableDetailInf line:recievableDetails)  {
			pAmount.add(
					line.getCurrency_code(), 
					line.getRecievable_amount()) ;
		}
		
		PaymentLine cashLine = null ;
		for (PaymentLine line:payments)  {
			if (PaymentMethod.CODE_CASH.equals(line.getPayment_method())) {
				cashLine = line ;
			}
			pAmount.deduct(line.getCurrency_code(), line.getPayment_amount()) ;
		}
		
		// 处理找零
		Double CNY_amount = pAmount.get(Currency.CODE_CHINA_YAN) ;
		if (CNY_amount != null && CNY_amount < 0) {
			if (cashLine == null) {
				throw new BusinessException("pay.more.than.need") ;
			} 
			
			if ((-CNY_amount) > cashLine.getPayment_amount()) {
				throw new BusinessException("return.more.than.cash") ;
			}
			
			cashLine.setPayment_amount(cashLine.getPayment_amount() + CNY_amount) ;
		}
		
		List<PaymentLine> paymentLines = new ArrayList<PaymentLine>() ;
		
		int i = 0 ;
		for (PaymentLine line:payments) {
			if (line.getPayment_amount() == null || line.getPayment_amount() == 0) {
				continue ;
			}
			line.setRef_source(ref_source) ;
			line.setRef_id(order_id) ;
			line.setLine_number(i ++) ;
			paymentLines.add(line) ;
			add(line, payableOrder) ;
		}
		
		payableOrder.updateByPayment(paymentLines, pAmount.isAllPaid()) ;
	}
	
	public void add(PaymentLine payment, Payable payableOrder) throws Exception {
		getPaymentLineBO().add(payment, payableOrder) ;
	}

	public List<PaymentLine> getLinesBySalesOrder(Long orderId) throws Exception {
		return getPaymentLineBO().getLines(C.REF_SOURCE_SALES_ORDER, orderId) ;
	}
	public List<PaymentLine> getLinesByPurchasesOrder(Long orderId) throws Exception {
		return getPaymentLineBO().getLines(C.REF_SOURCE_PURCHASES_ORDER, orderId) ;
	}
	
	public PaymentLineBO getPaymentLineBO() {
		if (paymentLineBO == null) {
			paymentLineBO = new PaymentLineBO();
			paymentLineBO.setJdbcDao(this.jdbcDao) ;
		}
		return paymentLineBO;
	}

	public void setPaymentLineBO(PaymentLineBO paymentLineBO) {
		this.paymentLineBO = paymentLineBO;
	}
	

	private class PaymentAmount extends HashMap<String, Double> {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public void add(String currencyCode, Double amount) {
			Double remain = this.get(currencyCode) ;
			
			if (remain == null) {
			    this.put(currencyCode, amount) ;
			} else {
				this.put(currencyCode, remain + amount) ;
			}
		}
		
		public void deduct(String currencyCode, Double amount) {
			if (amount == null) {
				return ;
			}
			
			Double remain = this.get(currencyCode) ;
			
			if (remain == null) {
			    this.put(currencyCode, -amount) ;
			} else {
				this.put(currencyCode, remain - amount) ;
			}
		}

        public boolean isAllPaid() {
        	for (double amount:this.values()) {
        		if (amount > 0) {
        			return false ;
        		}
        	}
        	return true ;
        }
	}
}
